Skip to content

feat(token-capture): add Megatron worker capture backend - #2823

Merged
ananthsub merged 15 commits into
NVIDIA-NeMo:mainfrom
lauradang:laurad/megatron-adapter
Sep 19, 2026
Merged

ananthsub merged 15 commits into
NVIDIA-NeMo:mainfrom
lauradang:laurad/megatron-adapter

Conversation

@lauradang

@lauradang lauradang commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add a megatron_worker external staging backend so Megatron-Inference (MInf) serving workers can participate in framework-owned token capture
  • extract the vLLM worker capture logic from the model server into typed backend handlers (external_capture.py) with a shared prepare/finalize lifecycle
  • both backends follow the same durability boundary: the worker stages the canonical token delta in TQ before responding, returns ng_commit_coords, and Gym records a token-free CallRecord only after that acknowledgement
  • the Megatron handler differs from vLLM only in the engine request flag (return_tokenized_data instead of return_tokens_as_token_ids); both strip token IDs, logprobs, and coordinates off the agent hop
  • let begin_call accept an explicit weight_version so the MInf worker can stamp the epoch from engine-finished metadata
  • add handler, config, and model-server integration coverage

Testing

  • pre-commit run --files on all changed working-tree files
  • Gym token-capture and focused model-server suites pass locally

Baseline note

The complete responses_api_models/vllm_model/tests/test_app.py run has 15 response-ID expectation failures (chtcmpl-123 versus resp_123). A representative failure reproduces unchanged on pthombre/tq-tokidcap-capture, so it is not introduced by this branch. The remaining red CI checks (lint, copyright, test_server_utils) come from files this PR does not touch, and the shard 2/6 failures are an apt mirror error.

Dependencies

🤖 Generated with Claude Code

@copy-pr-bot

copy-pr-bot Bot commented Aug 27, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@lauradang
lauradang marked this pull request as ready for review August 28, 2026 19:18
@github-actions github-actions Bot added the sla:triage-overdue Review assignment is over the one-business-day SLA label Aug 31, 2026
Comment thread nemo_gym/token_id_capture/adapters/megatron.py Outdated
@github-actions github-actions Bot removed the sla:triage-overdue Review assignment is over the one-business-day SLA label Sep 1, 2026
@lauradang lauradang changed the title feat(token-capture): add Megatron ledger capture backend feat(token-capture): add Megatron worker capture backend Sep 9, 2026
@lauradang
lauradang force-pushed the laurad/megatron-adapter branch from f344be6 to 77950f8 Compare September 10, 2026 19:16
Add the Megatron worker capture adapter and consolidate backend lifecycle handling. Preserve deferred-call custody and multi-turn lineage, keep capture metadata nested, strip compact prompt token echoes, and cover the shared backend lifecycle with parameterized tests.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Laura Dang <laurad@nvidia.com>
@lauradang
lauradang force-pushed the laurad/megatron-adapter branch from 77950f8 to 5f4ddab Compare September 10, 2026 19:33
@lauradang
lauradang requested a review from a team as a code owner September 10, 2026 19:33
@lauradang
lauradang changed the base branch from pthombre/tq-tokidcap-capture to main September 10, 2026 19:33

@pthombre pthombre left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1. Add a Gym-owned Megatron extraction adapter

Type: Coordinated Gym/RL design request

Could we add a dependency-light MegatronCaptureAdapter in Gym (nemo_gym/token_id_capture/adapters/megatron.py, mirroring adapters/vllm.py) and have RL #4129 use complete_call_from_response()?

Currently, TQMegatronTokenStager._stage_admitted() performs field extraction and numeric conversion outside Gym's extraction-failure boundary. Missing fields or conversion errors reach the stager's broad exception handler, which logs and returns None. Gym consequently records worker_response_missing_commit_coordinates, whereas equivalent vLLM extraction failures produce explicit capture_failed coordinates and worker_capture_failed.

Moving extraction under the shared wrapper would preserve that distinction and give Megatron payload extraction Gym-owned tests. It also gives Megatron an extract_extras seat; today the stager never passes extras, so routed-experts material is silently absent on MInf. TQ access and policy-epoch handling can remain in RL. Admission/lifecycle errors raised by begin_call still need explicit handling in the consumer; adding the adapter alone will not address those.

One protocol change is needed: CaptureAdapter in staging/protocols.py types response_payload as dict[str, Any], while MInf hands an offloaded payload object. Widening to Any is a runtime no-op since the Protocol is not runtime_checkable.

I would like the adapter file in this PR; the RL-side switch to complete_call_from_response() can follow in #4129.

Comment thread nemo_gym/token_id_capture/external_capture.py Outdated
Comment thread nemo_gym/token_id_capture/external_capture.py Outdated
Comment thread tests/unit_tests/test_external_capture_handlers.py Outdated
Resolve the vLLM model conflict with NVIDIA-NeMo#3382 (buffered SSE with external
token staging) by porting its stream guards into the capture handler
path and adapting the new streaming test to wrap the handler's
finalize_response.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Laura Dang <laurad@nvidia.com>
pthombre
pthombre previously approved these changes Sep 18, 2026
Comment thread nemo_gym/token_id_capture/adapters/megatron.py Outdated
Co-authored-by: Ananth Subramaniam <ananth.subramaniam@gmail.com>
Signed-off-by: Laura Dang <lauradang.2000@gmail.com>
@github-actions github-actions Bot removed the sla:review-overdue Review response is over the one-business-day SLA label Sep 18, 2026
Comment thread nemo_gym/token_id_capture/adapters/megatron.py Outdated
Comment thread nemo_gym/token_id_capture/adapters/megatron.py
Comment thread nemo_gym/token_id_capture/staging/records.py
assert "ng_capture" not in outbound
assert "return_tokens_as_token_ids" not in outbound

async def test_megatron_capture_handler_finalizes_through_chat_completions(self, monkeypatch: MonkeyPatch) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(for later) @pthombre we should have an end to end test with megatron and vllm in gym that can test token capture since current tests cannot detect mismatches in:

  • offload_params.ng_capture propagation through the actual Megatron Inference endpoint
  • Worker staging before HTTP acknowledgement
  • ng_commit_coords placement
  • Multi-turn prefix-chain resolution
  • Response conversion and stripping across Chat, Responses, Messages, and synthesized SSE

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Proposing a follow-up issue scoped to exactly these five mismatch classes (offload_params.ng_capture propagation, worker staging before HTTP ack, ng_commit_coords placement, multi-turn prefix-chain resolution, and response conversion/stripping across Chat, Responses, Messages, and synthesized SSE) with a real Megatron Inference and vLLM endpoint. Leaving this thread open as the tracker until that issue is filed.

Comment thread nemo_gym/token_id_capture/adapters/megatron.py Outdated

def extract_prompt_ids(self, response_payload: dict[str, Any]) -> list[int]:
"""Return the exact prompt token IDs used for generation."""
def extract_prompt_ids(self, response_payload: Any) -> list[int]:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pthombre potentially for later, we could better type this with a generic CaptureAdapter[PayloadT] to get some static typechecking help here in addition to relying on the adapter implementation to refine the type

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed this is worth doing as a follow-up. Note the scope is a bit wider than the protocol: install_capture takes adapter: CaptureAdapter | None, and the worker consumes the payload generically, so a PayloadT parameter only gives static help once the install path and worker are parametrized too. Leaving this thread open as the tracker until a follow-up issue exists.

Comment thread nemo_gym/token_id_capture/config.py Outdated
lauradang and others added 3 commits September 18, 2026 14:25
An unpoisoned receipt must name both terminal_model_call_id and
terminal_selection and carry no failure_reason. An unset
terminal_selection is reserved for receipts where no attribution stage
ran: such a receipt names no terminal and must be poisoned with a
reason. A poisoned receipt may still retain its terminal and method,
because attribution can succeed before a later call poisons the rollout.

The linearize compatibility wrapper now reports missing_terminal itself
instead of constructing a receipt the schema rejects.

Signed-off-by: Laura Dang <laurad@nvidia.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Laura Dang <laurad@nvidia.com>
…apture input

Validate element types in the Megatron adapter before conversion: token
id fields must contain only plain integers and log-probability fields
only numbers, so a float, string, or bool element poisons the call
instead of being silently coerced into a plausible id.

Reject megatron_worker capture requests whose messages carry non-text
content parts. The adapter stages no media geometry yet, so a
multimodal prompt would commit rows misaligned with the expanded engine
prompt.

Spell out Megatron Inference instead of the MInf shorthand.

Signed-off-by: Laura Dang <laurad@nvidia.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Laura Dang <laurad@nvidia.com>
…fig check

Signed-off-by: Laura Dang <laurad@nvidia.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Laura Dang <laurad@nvidia.com>
@ananthsub

Copy link
Copy Markdown
Contributor

/ok to test b601670

@ananthsub
ananthsub added this pull request to the merge queue Sep 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔄 Merge queue validation started! You can track the progress here: https://github.com/NVIDIA-NeMo/Gym/actions/runs/35408060167

Merged via the queue into NVIDIA-NeMo:main with commit 9fc05c0 Sep 19, 2026
41 checks passed
copy-pr-bot Bot pushed a commit to NVIDIA-NeMo/RL that referenced this pull request Sep 22, 2026
Move 3rdparty/Gym-workspace/Gym from the laurad/megatron-adapter fork branch
(37dc751f4) to NVIDIA-NeMo/Gym main at 9fc05c0ff, the squash merge of
NVIDIA-NeMo/Gym#2823 (Megatron worker capture backend). The pin now lives on
the submodule's configured upstream and branch, so CI and fresh clones can
fetch it. Every nemo_gym symbol imported from nemo_rl/ and tests/ resolves at
the new pin, and Gym's pyproject is identical between the two pins, so uv.lock
is unaffected.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Laura Dang <laurad@nvidia.com>

This branch was successfully deployed

1 active deployment
public — b601670a Deployed Sep 18, 2026 by copy-pr-bot[bot] via release / finalize / notify #3198
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants